import time
import logging
import threading
import paho.mqtt.client as mqtt

# 配置日志
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)

# MQTT 配置
MQTT_BROKER = "broker.hivemq.com"
MQTT_PORT = 1883
MQTT_TOPIC = "test/topic"
MQTT_USERNAME = None
MQTT_PASSWORD = None
RECONNECT_DELAY_MIN = 1
RECONNECT_DELAY_MAX = 60
FORCE_RECONNECT_INTERVAL = 3600  # 每小时强制重连一次（秒）

class MQTTClient:
    def __init__(self, broker, port, topic, username=None, password=None):
        self.broker = broker
        self.port = port
        self.topic = topic
        self.username = username
        self.password = password
        self._setup_client()
        self.reconnect_delay = RECONNECT_DELAY_MIN
        self._stop_event = threading.Event()
        self._last_connect_time = None

    def _setup_client(self):
        self.client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2)
        if self.username and self.password:
            self.client.username_pw_set(self.username, self.password)
        self.client.on_connect = self.on_connect
        self.client.on_disconnect = self.on_disconnect
        self.client.on_message = self.on_message
        # 可选：self.client.on_log = self.on_log

    def on_connect(self, client, userdata, flags, reason_code):
        if reason_code == 0:
            logger.info("✅ 成功连接到 MQTT 服务器")
            client.subscribe(self.topic)
            logger.info(f"📡 已订阅主题: {self.topic}")
            self.reconnect_delay = RECONNECT_DELAY_MIN
            self._last_connect_time = time.time()
        else:
            logger.error(f"❌ 连接失败，返回码: {reason_code}")

    def on_disconnect(self, client, userdata, disconnect_flags, reason_code, properties):
        logger.warning("⚠️ 与 MQTT 服务器断开连接")
        if not self._stop_event.is_set():
            self._reconnect_loop()

    def on_message(self, client, userdata, msg):
        logger.info(f"📬 收到消息 - 主题: {msg.topic}, 内容: {msg.payload.decode()}")

    def _reconnect_loop(self):
        while not self._stop_event.is_set():
            try:
                logger.info(f"🔁 尝试重新连接 (延迟 {self.reconnect_delay}s)...")
                self.client.reconnect()
                break
            except Exception as e:
                logger.error(f"🔄 重连失败: {e}")
                time.sleep(self.reconnect_delay)
                self.reconnect_delay = min(self.reconnect_delay * 2, RECONNECT_DELAY_MAX)

    def _force_reconnect_if_needed(self):
        """每小时强制重连一次"""
        while not self._stop_event.is_set():
            if (self._last_connect_time and
                time.time() - self._last_connect_time >= FORCE_RECONNECT_INTERVAL):
                logger.info("⏰ 到达定时重连时间，正在主动断开连接...")
                self.client.disconnect()  # 触发 on_disconnect，进而触发重连
                # 等待一段时间确保断开完成（可选）
                time.sleep(2)
            time.sleep(10)  # 每10秒检查一次

    def start(self):
        try:
            logger.info("🚀 启动 MQTT 客户端...")
            self.client.connect(self.broker, self.port, keepalive=60)
            self.client.loop_start()  # 启动后台网络循环（非阻塞）

            # 启动定时强制重连线程
            timer_thread = threading.Thread(target=self._force_reconnect_if_needed, daemon=True)
            timer_thread.start()

            # 主线程等待中断
            while not self._stop_event.is_set():
                time.sleep(1)

        except KeyboardInterrupt:
            logger.info("🛑 收到中断信号，正在关闭...")
        finally:
            self.stop()

    def stop(self):
        self._stop_event.set()
        self.client.loop_stop()
        self.client.disconnect()
        logger.info("🔌 MQTT 客户端已停止")
class TMqtt:
    @staticmethod
    def newMqtt( host , port , uid , pwd , topics , onMsg ):
        res = TMqtt()
        try:
            res.settings['host'] = host
            res.settings['port'] = port
            res.settings['uid'] = uid
            res.settings['pwd'] = pwd
            res.onMsg = onMsg
            res.settings['subTopics'] =[]
            if topics!=None and len(topics)>0:
                for t in topics:
                    res.settings['subTopics'].append(t)
        except Exception as err:
            print('new mqtt error.' , err)
        return  res

    def __init__(self):
        self.mqttCode = ""
        self.settings={
            "host":"",
            "port":0,
            "uid":"",
            "pwd":"" ,
            "subTopics":[],
            "reconnectTime": 5
        }
        self.client=  mqtt.Client()
        self.clientId = ""
        '''
        self.onConnectSuccess= None
        self.onConnectFail = None
        self.onDisConnect = None
        self.onMsg=None
        '''
        self.forceClosed = False
        self.onMsg = None
        self.tag ={}
    def reset(self):
        try:
            self.disconnect()
            self.connect()
        except Exception as er:
            print(er)
    def connect(self):
        res = {
            "status" :0
        }
        try:
            self.disconnect()
            self.forceClosed = False
            clientId = self.clientId if self.clientId!="" else 'mq-'+str(int(1000*time.time()))
            self.clientId = clientId
            host =  self.settings['host']
            port =  self.settings['port']
            uid =  self.settings['uid']
            pwd =  self.settings['pwd']
            #self.client=  mqtt.Client(mqtt.CallbackAPIVersion.VERSION2 , client_id= clientId)
            self.client=  mqtt.Client(client_id= clientId)
            self.client.on_connect = self.client.on_connect
            self.client.on_connect_fail = self.on_connect_fail
            self.client.on_disconnect = self.on_disconnect
            self.client.on_message = self.on_message
            self.client.username_pw_set(uid , pwd )
            code = self.client.connect(host, port, 60)
            if code ==0 :
                for topic in self.settings['subTopics']:
                    self.client.subscribe(topic)
            print(f'connect to mqtt server.{host}:{port}.[{code}]')
            self.client.loop_start()
            res['status']=1
        except Exception as er:
            print(er)
        return res
    def _reconnect_(self):
        res = {
            "status" :0
        }
        try:
            self.disconnect()
            self.forceClosed = False
            clientId = self.clientId if self.clientId!="" else 'mq-'+str(int(1000*time.time()))
            self.clientId = clientId
            host =  self.settings['host']
            port =  self.settings['port']
            uid =  self.settings['uid']
            pwd =  self.settings['pwd']
            #self.client=  mqtt.Client(mqtt.CallbackAPIVersion.VERSION2 , client_id= clientId)
            self.client=  mqtt.Client(client_id= clientId)
            self.client.on_connect = self.client.on_connect
            self.client.on_connect_fail = self.on_connect_fail
            self.client.on_disconnect = self.on_disconnect
            self.client.on_message = self.on_message
            self.client.username_pw_set(uid , pwd )
            code = self.client.connect(host, port, 60)
            if code ==0 :
                for topic in self.settings['subTopics']:
                    self.client.subscribe(topic)
            print(f'connect to mqtt server.{host}:{port}.[{code}]')
            self.client.loop_start()
            res['status']=1
        except Exception as er:
            print(er)
        return res
    def disconnect(self):
        res = {
            "status" :0
        }
        try:
            self.forceClosed = True
            self.client.disconnect()
            '''
            if( self.client!=None):
                if self.client.is_connected():
                    self.disconnect()
                self.client = None
            '''
        except Exception as er:
            print(er)
        return res
    def on_connect(self , client, userdata, flags, reason_code, properties):
        try:
            print(f"on mq client connected .code:{flags} , reason:{reason_code}")
            for topic in self.settings['subTopics']:
                self.client.subscribe(topic)
        except Exception as er:
            print(er)
    def on_connect_fail(self , client, userdata):
        try:
            print('---------  connect mqtt server fail!  ---------------')
            if not self.forceClosed:
                delay = self.settings['reconnectTime']
                time.sleep(delay)
                self.client.reconnect()
        except Exception as er:
            print(er)
    def on_disconnect(self , disconnect_flags, reason_code, properties):
        try:
            print('---------  disconnect from mqtt fail!  ---------------')
            print(f'disconnect_flags:{disconnect_flags}, reason_code:{reason_code}, properties:{properties}')
            print('-------------------------------------------------------')
            if not self.forceClosed:
                delay = self.settings['reconnectTime']
                time.sleep(delay)
                self.client.reconnect()
        except Exception as er:
            print(er)
    def on_message(self , client, userdata, message):
        try:
            if self.onMsg!=None:
                self.onMsg(message , self)
        except Exception as er:
            print(er)

from paho.mqtt import client as mqtt_client
class TMqttClient :
    def __init__(self):
        self._client : mqtt_client.Client = None
        self._clientId=""
        self.broker = ""
        self.port = 1883
        self.uid = ""
        self.pwd = ""
        self.forceReconnectTime = 0
        self.subTopics =[]
        self._reconnectDelay  = 1
        self._stop_event = threading.Event()
        self._last_connect_time = 0
        self.events ={
            "onEventMessage": self.onEventMessage ,
            "onEventConnected" : self.onEventConnected
        }
    def onEventMessage(self , msg , mqttClient):
        pass
    def onEventConnected(self , mqttClient):
        pass
    @property
    def clientId (self):
        try:
            if self._clientId == "" :
                self._clientId = f'mqid_{int(time.time())}'
        except Exception as er:
            print(f" mqtt clientId   error" , er)
        return self._clientId
    def _reconnect_loop(self):
        while not self._stop_event.is_set():
            try:
                print (f"🔁 尝试重新连接 (延迟 {self._reconnectDelay}s)...")
                self._client.reconnect()
                break
            except Exception as e:
                print(f"🔄 重连失败: {e}")
                time.sleep(self._reconnectDelay)
                self._reconnectDelay = min(self._reconnectDelay * 2, 3600)
    def connect(self):
        try:
            if self._client == None :
                _client = mqtt_client.Client(mqtt.CallbackAPIVersion.VERSION2)
                _client.on_connect = self.on_connect
                _client.on_connect_fail = self.on_connect_fail
                _client.on_disconnect = self.on_disconnect
                _client.on_message = self.on_message
                _client.username_pw_set(self.uid , self.pwd)
                self._reconnectDelay = 1
                self._last_connect_time = time.time()
                self._client = _client
                _client.connect( self.broker , self.port)
                _client.loop_start()
        except Exception as er:
            print(f" mqtt create mqtt client error" , er)
        return self._client
    def disConnect(self):
        try:
            self._stop_event.set()
            self._client.loop_stop()
            self._client.disconnect()
        except Exception as er:
            print(f" mqtt create mqtt client error" , er)
        return self._client
    def forceReConnect(self):
        try:
            while not self._stop_event.is_set():
                if self.forceReconnectTime>0:
                    dltTimeFlag = time.time() - self._last_connect_time >= self.forceReconnectTime
                    if dltTimeFlag :
                        print("⏰ 到达定时重连时间，正在主动断开连接...")
                        self._client.disconnect()  # 触发 on_disconnect，进而触发重连
                        # 等待一段时间确保断开完成（可选）
                        time.sleep(5)
                time.sleep(10)  # 每10秒检查一次
        except Exception as er:
            print(f" mqtt create mqtt client error" , er)
    def on_connect(self , client, userdata, flags, reason_code, properties):
        try:
            print(f"on mq client connected .code:{flags} , reason:{reason_code}")
            if reason_code =='Success' :
                self._reconnectDelay = 1
                self._last_connect_time = time.time()
                try:
                    handler = self.events['onEventConnected']
                    handler( self)
                except Exception as eer:
                    print(f"before connect error" , eer)
                for topic in self.subTopics:
                    self._client.subscribe(topic)
                print(f'sub topics:' , self.subTopics)
        except Exception as er:
            print(er)
    def on_connect_fail(self , client, userdata):
        try:
            print('---------  connect mqtt server fail!  ---------------')
            self._reconnect_loop()
        except Exception as er:
            print(er)
    def on_disconnect(self , disconnect_flags, reason_code, properties):
        try:
            print('---------  disconnect from mqtt fail!  ---------------')
            print(f'disconnect_flags:{disconnect_flags}, reason_code:{reason_code}, properties:{properties}')
            print('-------------------------------------------------------')
            if not self._stop_event.is_set():
                self._reconnect_loop()
        except Exception as er:
            print(er)
    def on_message(self , client, userdata, message):
        try:
            if self.events['onEventMessage']!=None :
                handler = self.events['onEventMessage']
                handler(message , self)
        except Exception as er:
            print(er)
